<template>
  <el-form
    ref="form"
    class="page-form"
    :class="className"
    :model="formData"
    :rules="rules"
    :label-width="getLabelWidth()"
    @submit.stop.prevent
  >
    <el-row type="flex" class="flex-wrap" :gutter="10">
      <el-col
        v-for="(item) in getColumns()"
        :key="item.prop"
        :span="item.span || 23"
      >
        <el-form-item
          v-bind="item"
          :label="colon && item.label ? `${item.label}：` : item.label"
          :rules="getRules(item)"
          :label-width="getItemWidth(item)"
          :class="item.className"
        >
          <!-- solt -->
          <template v-if="item.type === 'slot'">
            <slot :name="item.prop" />
          </template>
          <!-- 文本 -->
          <template v-if="item.type === 'text'">
            <span :style="item.style" class="default-text">{{ getContent(item) }}</span>
          </template>
          <!-- 普通输入框 -->
          <el-input
            v-if="item.type === 'input'"
            v-model.trim="formData[item.prop]"
            :type="item.inputType || 'text'"
            :disabled="item.disabled"
            :placeholder="getPlaceholder(item)"
            :min="item.min"
            :max="item.max"
            clearable
            @change="handleEvent(item)"
          >
            <template #append v-if="item.append">{{ item.append }}</template>
          </el-input>
          <!-- 文本输入框 -->
          <el-input
            v-if="item.type === 'textarea'"
            v-model.trim="formData[item.prop]"
            :type="item.type"
            :disabled="item.disabled"
            :placeholder="getPlaceholder(item)"
            :autosize="{minRows: 2, maxRows: 10}"
            clearable
            @change="handleEvent(item)"
          >
            <template #append v-if="item.append">{{ item.append }}</template>
          </el-input>
          <!-- 计数器 -->
          <el-input-number
            v-if="item.type === 'inputNumber'"
            v-model="formData[item.prop]"
            :min="item.min"
            :max="item.max"
            :step="item.step || 1"
            :precision="item.precision || 0"
            @change="handleEvent(item)"
          />
          <!-- 选择框 -->
          <el-select
            v-if="item.type === 'select'"
            v-model="formData[item.prop]"
            :disabled="item.disabled || false"
            :clearable="item.clearable || true"
            :filterable="item.filterable || false"
            :multiple="item.multiple || false"
            :placeholder="getPlaceholder(item)"
            style="width: 100%"
            @change="handleSelectChange(item)"
          >
            <el-option
              v-for="(childItem) in item.list"
              :key="childItem[item.key || item.valueKey || 'val']"
              :label="childItem[item.labelKey || 'label']"
              :value="childItem[item.valueKey || 'val']"
            />
          </el-select>
          <el-select-v2
            v-if="item.type === 'virtual-select'"
            v-model="formData[item.prop]"
            :options="getVirtualSelectOptions(item)"
            :disabled="item.disabled || false"
            :clearable="item.clearable || true"
            :filterable="item.filterable || false"
            :multiple="item.multiple || false"
            :placeholder="getPlaceholder(item)"
            style="width: 100%"
            @change="handleSelectChange(item)"
          >
          </el-select-v2>
          <el-radio-group
            v-if="item.type === 'radio'"
            v-model="formData[item.prop]"
            @change="handleEvent(item)"
          >
            <el-radio
              v-for="radio in item.list"
              :key="radio.label"
              :label="radio.val"
            >
              {{ radio.label }}
            </el-radio>
          </el-radio-group>
          <!-- 日期选择框 -->
          <el-date-picker
            v-if="item.type === 'date'"
            v-model="formData[item.prop]"
            range-separator="至"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
            :type="item.dateType"
            :shortcuts="item.shortcuts"
            :clearable="item.clearable || true"
            :disabled="item.disabled"
            :disabledDate="item.disabledDate"
            :value-format="item.valueFormat || 'YYYY-MM-DD HH:mm:ss'"
            :placeholder="getPlaceholder(item)"
            :popper-class="item.popperClass"
            style="width: 100%"
            @change="handleEvent(item)"
            @clear="handleEvent(item)"
          />
          <el-button
            v-bind="$attrs"
            v-if="item.type === 'search' || item.type === 'btn'"
            :type="item.btnType || 'primary'"
            @click="handleSingleButtonSearch(item)"
          >
            {{ item.text || '搜索' }}
          </el-button>
          <template v-if="item.type === 'btns'">
            <template v-for="(btn, index) in item.btns" :key="index">
              <slot v-if="!btn.slot">
                <el-button
                  v-bind="btn"
                  :type="btn.type"
                  :disabled="btn.disabled"
                  @click="handleMultipleButtonSearch(btn)"
                >
                  {{ btn.text || '搜索' }}
                </el-button>
              </slot>
              <slot v-else :name="btn.prop"></slot>
            </template>
          </template>
        </el-form-item>
      </el-col>
    </el-row>
  </el-form>
</template>

<script>
import { reactive, toRefs, watch } from 'vue';

const phoneReg = /^1[3-9]\d{9}/;

export default {
  name: 'BasicForm',
  props: {
    columns: {
      type: Array,
      default: () => ([]),
    },
    modelValue: {
      type: Object,
      default: () => ({}),
    },
    className: String,
    labelWidth: {
      type: [String, Number],
      default: '110px',
    },
    colon: Boolean,
    rules: Object,
    needSearch: Boolean,
  },
  setup(props, { emit }) {
    const state = reactive({
      formData: {},
    });

    watch(
      () => props.modelValue,
      obj => {
        state.formData = obj;
      },
      {
        immediate: true,
        deep: true,
      },
    );
    const getVirtualSelectOptions = (item = {}) => {
      if (!item.list || !item.list.length) return [];
      return item.list.map(row => ({ ...row, label: row[item.labelKey || 'label'], value: row[item.valueKey || 'value'] }));
    };
    const getLabelWidth = () => {
      if (!props.labelWidth.endsWith('px')) {
        return `${props.labelWidth}px`;
      }
      return props.labelWidth;
    };
    const getItemWidth = (item) => {
      if (item.labelWidth) return item.labelWidth;
      if (item.label) return props.labelWidth;
      return '10px';
    };
    const getColumns = () => props.columns;
    const getContent = (item) => {
      if (item.format) {
        return item.format(item);
      }
      return item.unit ? (state.formData[item.prop] || '') + item.unit : state.formData[item.prop];
    };
    const getPlaceholder = (item) => {
      if (item.placeholder) return item.placeholder;
      if (item.type === 'select' || item.type === 'date') {
        return '请选择';
      }
      return '请输入';
    };
    const handleSelectChange = (item) => {
      if (item.onChange) item.onChange(state.formData[item.prop]);
      else handleEvent(item);
    };
    const handleEvent = (item) => {
      if (item.needSearch) handleSearch(item);
    };
    // 如果是type === 'btn'
    const handleSingleButtonSearch = (item) => {
      if (item.type === 'btn' && item.onClick) item.onClick();
      else handleSearch(item);
    };
    // 如果是type === 'btns'
    const handleMultipleButtonSearch = (btn) => {
      if (btn.custom) {
        // if (btn.onClick) btn.onClick();
        return;
      }
      handleSearch(btn);
    };
    const handleSearch = (item) => {
      emit('handleEvent', item);
    };
    const validatePhone = (item, value, cb) => {
      if (state.formData[item.prop] && state.formData[item.prop].length !== 11) {
        return cb(new Error('手机号码长度必须为11位数'));
      }
      if (state.formData[item.prop] && !phoneReg.test(state.formData[item.prop])) {
        return cb(new Error('手机号码格式不正确'));
      }
      return cb();
    };
    const getRules = (item) => {
      if (item.validateType === 'phone') {
        return [
          ...(item.rules || []),
          { trigger: 'blur', validator: (rule, value, cb) => validatePhone(item, value, cb) },
          ...(item.required ? [{
            required: item.required,
            message: getPlaceholder(item),
          }] : []),
        ];
      }
      return [
        ...(item.rules || []),
        ...(item.required ? [{
          required: item.required,
          message: getPlaceholder(item),
        }] : []),
      ];
    };

    return {
      // data
      ...toRefs(state),

      // methods
      getVirtualSelectOptions,
      getLabelWidth,
      getItemWidth,
      getColumns,
      getContent,
      getPlaceholder,
      handleSelectChange,
      handleEvent,
      handleSingleButtonSearch,
      handleMultipleButtonSearch,
      handleSearch,
      validatePhone,
      getRules,
    };
  },
  emits: [
    'handleEvent',
    'update:modelValue',
  ],
  methods: {
  },
};
</script>

<style lang="less" scoped>
.default-text {
  color: #333;
}
:deep([type="btns"] .el-form-item__content) {
  flex-wrap: nowrap;
}
:deep(.el-form-item) {
  margin-right: 0;
  width: 100%;
}
</style>
